"use strict";

const renderContext = window.top; // 渲染组件的环境(window)
const FileMap = "_components_file_map"; // 保存组件的对象
const ISC = "_is_component"; // 组件标识
const ISCE = "_is_component_extend"; // 组件标识
const resolveSy = "_component_resolve"; // 组件加载完标识

const watchSy = Symbol("watch");
const VueResSy = Symbol("VueResolve");
let VueResT = () => {};
const Vue = new Promise((resolve) => (VueResT = resolve));
Vue[VueResSy] = VueResT;

const fileMap = (renderContext[FileMap] = renderContext[FileMap] || {});

/**
 * 在渲染环境加载umd文件
 * @param {string} src umd文件路径
 * @param {string} name 全局注册的key
 * @returns
 */
export const importUmd = async (src, name) => {
  if (!renderContext[name]) {
    await import(src);
    renderContext[name] = window[name];
  }
  return renderContext[name];
};

/**
 * 在渲染环境加载es文件
 * @param {string} src es文件路径
 * @returns
 */
export const importEs = async (src) => {
  const name = `es:${src}`;

  if (!fileMap[name]) {
    fileMap[name] = await import(src);
  }
  return fileMap[name];
};
/**
 * 在渲染环境加载link标签
 * @param {string} src link文件路径
 */
export const importLink = (src) => {
  src = /^[A-z]/.test(src)
    ? `/node_modules/${src}`
    : new URL(src, window.location.href).toString();
  const name = `link:${src}`;

  if (!fileMap[name]) {
    fileMap[name] = true;
    const document = renderContext.document;
    const linkDom = document.createElement("link");
    linkDom.setAttribute("rel", "stylesheet");
    linkDom.setAttribute("href", src);
    document.querySelector("head").appendChild(linkDom);
  }
};
/**
 * 在渲染环境加载样式
 * @param {string} str 样式内容
 * @param {string} source 样式来源路径
 */
export const importStyle = (str = "", source) => {
  const document = renderContext.document;
  const styleDom = document.createElement("style");
  styleDom.setAttribute("type", "text/css");
  if (source) styleDom.setAttribute("source-src", source);
  styleDom.textContent = str;
  document.querySelector("head").appendChild(styleDom);
};

/**
 * 加载异步组件
 * @param {string} url '组件路径'
 * @param {'import' | 'extend'} type 组件使用方式
 * @returns {HTMLIFrameElement} iframe节点
 */
export const loaderComponent = async (url, type) => {
  const _url = new URL(url);
  // _url.searchParams.append(CN, props.name);
  if (type === "import") _url.searchParams.append(ISC, "true");
  else if (type === "extend") _url.searchParams.append(ISCE, "true");

  const node = document.createElement("iframe");
  node.src = _url.toString();
  node.style.display = "none";

  // 加载style
  node.onload = () => {
    const styles = node.contentDocument.querySelectorAll("style");
    styles.forEach((item) => importStyle(item.textContent, node.src));
  };

  return node;
};

/**
 * <自定义加载>组件
 */
class ImportElement extends HTMLElement {
  // 封闭状态,只有这样才能访问shadowRoot
  #shadowRoot;
  #props = { src: "", name: "", type: "" };
  constructor() {
    super();
    //创建阴影DOM
    this.#shadowRoot = this.attachShadow({ mode: "closed" });
  }
  static get observedAttributes() {
    return ["src", "name", "type"];
  }
  attributeChangedCallback(key, val0, valN) {
    this.#props[key] = valN;
  }
  connectedCallback() {
    this.render();
  }
  async render() {
    const { defineAsyncComponent } = await importEs("vue");
    const props = this.#props;
    const _url = new URL(props.src, this.baseURI);
    const filePath = _url.pathname;
    const name = `import:${filePath}`;

    if (!fileMap[name]) {
      let resolveT = () => {};
      const Pro = new Promise((resolve) => (resolveT = resolve));
      fileMap[name] = defineAsyncComponent(() => {
        loaderComponent(_url, "import").then((node) => {
          this.#shadowRoot.appendChild(node);
          node.addEventListener("load", () => node.remove());
        });

        return Pro;
      });
      //把触发方法放在Promise上
      fileMap[name][resolveSy] = resolveT;
    }
    this.selfcomponent = fileMap[name];
  }
}
//注册组件
customElements.define("import-vue", ImportElement);

/**
 * <自定义扩展>组件
 */
class ExtendElement extends HTMLElement {
  // 封闭状态,只有这样才能访问shadowRoot
  #shadowRoot;
  #props = { src: "", name: "", type: "" };
  constructor() {
    super();
    //创建阴影DOM
    this.#shadowRoot = this.attachShadow({ mode: "closed" });
  }
  static get observedAttributes() {
    return ["src", "name", "type"];
  }
  attributeChangedCallback(key, val0, valN) {
    this.#props[key] = valN;
  }
  connectedCallback() {
    this.render();
  }
  async render() {
    const props = this.#props;
    const _url = new URL(props.src, this.baseURI);
    const filePath = _url.pathname;
    const name = `extend:${filePath}`;

    if (!fileMap[name]) {
      let resolveT = () => {};
      fileMap[name] = new Promise((resolve) => (resolveT = resolve));
      //把触发方法放在Promise上
      fileMap[name][resolveSy] = resolveT;
      loaderComponent(_url, "extend").then((node) => {
        this.#shadowRoot.appendChild(node);
        node.addEventListener("load", () => node.remove());
      });
    }
    this.selfcomponent = fileMap[name];
  }
}
//注册组件
customElements.define("extend-vue", ExtendElement);

/**
 * 注册Vue组件
 */
/**
 *
 * @param {object} pageData
 * @returns vue实例/vue组件
 */
/**
 *
 * @param {object} pageData
 * @param {{install:Function}} param1
 * @returns
 */
export const createVue = async (pageData, opts) => {
  Vue[VueResSy](opts.Vue);
  const { install } = opts;

  const { compile, createApp, watchEffect } = opts.Vue;
  await sleep();

  const _url = new URL(window.location.href);
  const hasKey = (key) => _url.searchParams.get(key) === "true";

  let template = pageData.template;
  if (!template) {
    template = document.querySelector("template")?.innerHTML;
  } else if (/^#/.test(template)) {
    template = document.querySelector(template)?.innerHTML;
  } else if (template instanceof HTMLElement) {
    template = template.innerHTML;
  }
  pageData.template = template;

  document.querySelectorAll("import-vue").forEach((node) => {
    const _name = node.getAttribute("name");
    const _component = node.selfcomponent;
    if (_name && _component) {
      if (!pageData.components) pageData.components = {};
      pageData.components[_name] = _component;
    }
  });

  const componentExtends = [...document.querySelectorAll("extend-vue")].map(
    (node) => node.selfcomponent
  );
  const pageDataExtends = (await Promise.all(componentExtends)).filter(
    (ce) => ce
  );
  const mergeData = deepMerge({}, pageData, ...pageDataExtends);
  const mergeTemplate = templateMerge(
    template,
    pageDataExtends.map((ce) => ce.template)
  );

  // 扩展组件
  if (hasKey(ISCE)) {
    const filePath = _url.pathname;
    // 触发异步组件加载的resolve
    fileMap[`extend:${filePath}`]?.[resolveSy]?.(mergeData);
    return mergeData;
  }

  const dataKeys = [];
  const dataGetEntries = [];
  const methodKeys = [];
  const watchKeys = [];

  Reflect.ownKeys(mergeData).forEach((key) => {
    if (["template", "components", "computed", "watch"].includes(key)) return;
    if (isFunction(mergeData[key])) {
      methodKeys.push(key);
      if (/^watch\$/.test(key)) watchKeys.push(key);
    } else {
      dataKeys.push(key);
      const desc = Object.getOwnPropertyDescriptor(mergeData, key);
      if ("value" in desc) dataKeys.push(key);
      else dataGetEntries.push([key, desc]);
    }
  });

  methodKeys.forEach((key) => {
    if (!mergeData.methods) mergeData.methods = {};
    mergeData.methods[key] = mergeData[key];
  });

  const component = {
    inheritAttrs: false,
    render: compile(mergeTemplate),

    data() {
      const data = {};

      dataKeys.forEach((key) => {
        data[key] = cloneDeep(mergeData[key]);
      });
      dataGetEntries.forEach(([key, val]) => {
        const { configurable, enumerable, get, set } = val;
        Object.defineProperty(data, key, {
          configurable,
          enumerable,
          get: get?.bind(this),
          set: set?.bind(this),
        });
      });

      return data;
    },
    components: mergeData.components,
    computed: mergeData.computed,
    watch: mergeData.watch,
    methods: mergeData.methods,
    created() {
      this[watchSy] = [];
      watchKeys.forEach((key) => {
        this[watchSy].push(watchEffect(this[key]));
      });
    },
    beforeUnmount() {
      this[watchSy].forEach((item) => item.stop());
    },
  };

  // 子组件
  if (hasKey(ISC)) {
    const filePath = _url.pathname;
    // 触发异步组件加载的resolve
    fileMap[`import:${filePath}`]?.[resolveSy]?.(component);
    return component;
  }
  // 主组件
  else {
    const app = createApp(component);
    //全局注册组件
    await install(app);
    app.mount("#app");
    return app;
  }
};

// 辅助函数
// 判断是否为纯对象
function isPlainObject(value) {
  return (
    value &&
    typeof value === "object" &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === "[object Object]"
  );
}

// 深度合并函数，保留所有层级的 get/set 访问器，且不修改源对象
function deepMerge(target, ...sources) {
  // 创建目标对象的浅拷贝，避免修改原目标对象
  const newTarget = isPlainObject(target) ? { ...target } : target;

  sources.forEach((source) => {
    if (!isPlainObject(source)) return;

    // 获取源对象所有属性的描述符
    const descriptors = Object.getOwnPropertyDescriptors(source);

    Object.keys(descriptors).forEach((key) => {
      const descriptor = descriptors[key];
      const targetValue = newTarget[key];

      // 如果是嵌套对象且两边都是纯对象，则递归合并（创建新对象）
      if (isPlainObject(descriptor.value) && isPlainObject(targetValue)) {
        // 递归合并并创建新对象，避免修改原对象
        newTarget[key] = deepMerge({}, targetValue, descriptor.value);
      } else {
        // 否则直接定义属性（保留访问器）
        Object.defineProperty(newTarget, key, descriptor);
      }
    });
  });

  return newTarget;
}

// 深度克隆函数，保留所有层级的 get/set 访问器，且不修改源对象
function cloneDeep(source) {
  // 处理数组
  if (Array.isArray(source)) {
    // 对数组的每个元素进行深度克隆
    return source.map((item) => cloneDeep(item));
  }

  // 处理非对象/非数组类型（直接返回原始值）
  if (!isPlainObject(source)) {
    return source;
  }

  // 处理纯对象（包括get/set访问器）
  const cloned = {};
  const descriptors = Object.getOwnPropertyDescriptors(source);

  Object.keys(descriptors).forEach((key) => {
    const descriptor = descriptors[key];

    // 如果属性值是对象或数组，则递归克隆
    if (isPlainObject(descriptor.value) || Array.isArray(descriptor.value)) {
      descriptor.value = cloneDeep(descriptor.value);
    }

    // 为克隆对象定义属性（保留访问器）
    Object.defineProperty(cloned, key, descriptor);
  });

  return cloned;
}

function isFunction(value) {
  // 处理 null 和 undefined 情况
  if (value == null) {
    return false;
  }

  // 核心判断：使用 Object.prototype.toString 检测函数类型
  const type = Object.prototype.toString.call(value);
  return type === "[object Function]" || type === "[object AsyncFunction]";
}

// 暂停一会
function sleep(time = 0) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}

// 合并模板
function templateMerge(target = "", _extends = []) {
  if (!_extends.length) return target;

  const range = document.createRange();
  const targetDoc = range.createContextualFragment(target);

  _extends.forEach((extend) => {
    if (!extend) return;

    const extendDoc = range.createContextualFragment(extend);
    const reviseList = extendDoc.querySelectorAll("x-revise,[x-revise]");

    reviseList.forEach((node) => {
      const attrs = {};
      [...node.attributes].forEach(({ name, value }) => {
        attrs[name] = value;
      });

      const {
        "x-revise": xRevise,
        "x-path": xPath,
        "x-type": xType,
        ...props
      } = attrs;
      const reviseNode = targetDoc.querySelector(xPath);

      if (!reviseNode) {
        console.warn(`没有查找到操作节点: ${xPath} `);
        return;
      }

      const fragment = document.createDocumentFragment();
      if (
        [
          "append",
          "prepend",
          "insert-before",
          "insert-after", // 插入到节点之后
          "replace", // 替换节点
        ].includes(xType)
      ) {
        [...node.childNodes].forEach((nodeC) => {
          fragment.appendChild(nodeC.cloneNode(true));
        });
      }
      // 插入到末尾
      if (xType === "append") {
        reviseNode.appendChild(fragment);
      }
      // 插入到开头
      else if (xType === "prepend") {
        if (reviseNode.children.length > 0) {
          reviseNode.insertBefore(fragment, reviseNode.children[0]);
        } else {
          reviseNode.appendChild(fragment);
        }
      }
      // 插入到节点之前
      else if (xType === "insert-before") {
        const reviseNodeP = reviseNode.parentNode;
        if (!reviseNodeP) {
          console.warn(`没有查找到操作节点: ${xPath} 的父节点`);
          return;
        }

        reviseNodeP.insertBefore(fragment, reviseNode);
      }
      // 插入到节点之后
      else if (xType === "insert-after") {
        const reviseNodeP = reviseNode.parentNode;
        if (!reviseNodeP) {
          console.warn(`没有查找到操作节点: ${xPath} 的父节点`);
          return;
        }
        if (reviseNode.nextSibling === null) {
          reviseNodeP.appendChild(fragment);
        } else {
          parentNode.insertBefore(fragment, reviseNode.nextSibling);
        }
      }
      // 替换节点
      else if (xType === "replace") {
        reviseNode.replaceWith(fragment);
      }
      // 删除节点
      else if (xType === "remove") {
        reviseNode.remove();
      }
      // 删除所有子节点(清空)
      else if (xType === "empty") {
        reviseNode.textContent = "";
      }
      // 深度合并
      else if (xType === "deep-merge") {
        // ====
      }

      // 修改属性
      for (const key in props) {
        if (!Object.hasOwn(props, key)) continue;

        if (/^:/.test(key)) {
          reviseNode.removeAttribute(key.replace(/^:/, ""));
        } else {
          reviseNode.removeAttribute(`:${key}`);
        }
        reviseNode.setAttribute(key, props[key]);
      }
    });
  });

  const tempDiv = document.createElement("div");
  tempDiv.appendChild(targetDoc);
  return tempDiv.innerHTML;
}
